/* Time_Linux.ipp
 * 
 *  Author          : Alexander J. Yee
 *  Date Created    : 09/17/2014
 *  Last Modified   : 03/07/2016
 * 
 */

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//  Dependencies
#include <time.h>
#include <unistd.h>
#include <sys/times.h>
#include "PublicLibs/ConsoleIO/Label.h"
#include "Time_Posix.h"
namespace ymp{
namespace Time{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void CompileOptions(){
    Console::println_labelm("Time", "POSIX", 'G');
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
YM_NO_INLINE WallClock WallClock::now(){
    WallClock out;
    if (gettimeofday(&out.m_time, NULL)){
        Console::warning("Unable to access gettimeofday().");
        Console::quit_program(1);
    }
    return out;
}
double WallClock::operator-(const WallClock& x) const{
    u64_t isec = (u64_t)m_time.tv_sec - (u64_t)x.m_time.tv_sec;
    s32_t usec = m_time.tv_usec - x.m_time.tv_usec;
    if (usec < 0){
        usec += 1000000;
        isec--;
    }
    return (double)isec + (double)usec * .000001;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
PerformanceTimeStamp PerformanceTimeStamp::now(){
    PerformanceTimeStamp out;
    out.wall_clock = WallClock::now();

    struct tms timers;
    if (times(&timers) == (clock_t)-1){
        out.user_clock_ticks = 0;
        out.kernel_clock_ticks = 0;
    }else{
        out.user_clock_ticks   = timers.tms_utime;
        out.kernel_clock_ticks = timers.tms_stime;
    }
    return out;
}
void PerformanceTimeDuration::reset(){
    wall_time = 0;
    user_time = 0;
    kernel_time = 0;
}
PerformanceTimeDuration PerformanceTimeDuration::time_since(const PerformanceTimeStamp& timestamp){
    return PerformanceTimeStamp::now() - timestamp;
}
void PerformanceTimeDuration::operator+=(const PerformanceTimeDuration& duration){
    wall_time += duration.wall_time;
    user_time += duration.user_time;
    kernel_time += duration.kernel_time;
}
PerformanceTimeDuration operator-(const PerformanceTimeStamp& end, const PerformanceTimeStamp& start){
    static double RATIO = 1. / sysconf(_SC_CLK_TCK);

    PerformanceTimeDuration out;
    out.wall_time = end.wall_clock - start.wall_clock;
    out.user_time = (end.user_clock_ticks - start.user_clock_ticks) * RATIO;
    out.kernel_time = (end.kernel_clock_ticks - start.kernel_clock_ticks) * RATIO;
    return out;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
YM_NO_INLINE void get_process_times(double& user, double& kernel){
    user = 0;
    kernel = 0;
}
YM_NO_INLINE std::string tostr_now(){
    time_t rawtime = time(nullptr);

    char buffer[32];

    const char* current_time = asctime(localtime(&rawtime));
    char ch;
    upL_t c = 0;
    do{
        ch = *current_time;
        buffer[c++] = *current_time++;
    }while (ch != '\0');

    c = 0;
    while (buffer[c] >= 32)
        c++;
    buffer[c] = '\0';

    return buffer;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
}
}
